package realarm.hardwaretest.jniinterface;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class SerialPortManager {

    private static final String TAG = SerialPortManager.class.getSimpleName();
    private static final int MSG_ERROR = 0xF0;
    private static final int MSG_READ = 0xF1;
    private static final int MSG_WRITE = 0xF2;
    private static final int KEEP_DOG = 0xF3;

    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private SerialPort mSerialPort;
    private ReadSerialMessageThread readSerialMessageThread;
    private List<SerialPortListener> mListeners = new ArrayList<>();

    // ic卡数据长度，6个字节
    private static final int SIZE = 6;
    // 超时时间，200ms
    private static final long TIME_OUT = 200;
    private byte[] mData;
    private long mLastTime = 0L;
    private int mCount = 0;


    public static final int CMD_TYPE_WATCH_DOG = 0xA0;
    public static final int CMD_TYPE_ASYNC_HANDSHAKE = 0xA1;
    public static final int CMD_TYPE_ADD_DATA = 0xA4;
    public static final int CMD_TYPE_DELETE_DATA = 0xA5;
    public static final int CMD_TYPE_QUERY_DATA = 0xA6;
    private static final String ACK_WATCH_DOG_SUCCESS = "fbf0e001d1fe";
    private static final String ACK_WATCH_DOG_CLOSE_SUCCESS = "fbf0e101d2fe";
    private static final String ACK_ASYNC_HANDSHAKE_SUCCESS = "fbf1a00192fe";
    private static final String ACK_ASYNC_DATA_SUCCESS = "fbf1a10193fe";
    private static final String ACK_ASYNC_END_SUCCESS = "FBF1A20194FE";
    private static final String ACK_ADD_DATA_SUCCESS = "fbf20001f3fe";
    private static final String ACK_DELETE_DATA_SUCCESS = "FBF30001F4FE";

    private int mCmdType;

    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case KEEP_DOG://看门狗喂狗
                    mHandler.sendEmptyMessageDelayed(KEEP_DOG, 10000);
                   keepDogHandshake();
                    break;
                case MSG_READ:
                    // 读取数据
                    String ack = (String) msg.obj;
                    if(ack.startsWith("fbf6")){
                        Log.d(TAG, "handleMessage: "+ack);
                        break;
                    }
                    switch (mCmdType) {
                        case CMD_TYPE_WATCH_DOG:
                            if (ack.startsWith("fbf0")) {
                                if (ACK_WATCH_DOG_SUCCESS.equalsIgnoreCase(ack)) {
                                    Log.e(TAG, "看门狗握手成功");
                                    showInfo("看门狗握手成功");
                                    mHandler.sendEmptyMessageDelayed(KEEP_DOG,3000);
                                } else if (ACK_WATCH_DOG_CLOSE_SUCCESS.equalsIgnoreCase(ack)) {
                                    Log.e(TAG, "看门狗断开握手成功");
                                    showInfo("看门狗断开握手成功");
                                   mHandler.removeMessages(KEEP_DOG);
                                }
                            }
                            break;
                        case CMD_TYPE_ASYNC_HANDSHAKE:   //关于同步更新的反馈
                            if (ack.startsWith("fbf1a0")) {
                                if (ACK_ASYNC_HANDSHAKE_SUCCESS.equalsIgnoreCase(ack)) {
                                    Log.e(TAG, "同步更新握手成功");
                                    showInfo("同步更新握手成功");
                                    next();//开始写入数据
                                } else {
                                    showInfo("同步更新握手失败");
                                }

                            } else if (ack.startsWith("fbf1a1")) {
                                if (ACK_ASYNC_DATA_SUCCESS.equalsIgnoreCase(ack)) {
                                    Log.e(TAG, "同步更新写入一条数据成功");
                                    showInfo("同步更新插入数据" + cmds[index] + "成功");
                                    index++;
                                    if (index < MAX) {
                                        next();
                                    } else {
                                        asyncEnd(index);
                                    }
                                } else {
                                    showInfo("同步更新插入数据" + cmds[index] + "失败");
                                    index++;
                                    if (index < MAX) {
                                        next();
                                    } else {
                                        asyncEnd(index);
                                    }
                                }
                            } else if (ack.startsWith("fbf1a2")) {
                                if (ACK_ASYNC_END_SUCCESS.equalsIgnoreCase(ack)) {
                                    showInfo("同步更新插入数据结束");
                                    Log.e(TAG, "同步更新所有数据成功");

                                } else {
                                    Log.e(TAG, "同步更新所有数据结束");
                                }
                            }
                            break;
                        case CMD_TYPE_ADD_DATA:
                            if (ACK_ADD_DATA_SUCCESS.equalsIgnoreCase(ack)) {
                                Log.e(TAG, "增加一条数据成功");
                                showInfo("增加成功");
                            } else {
                                showInfo("增加失败");
                            }
                            break;
                        case CMD_TYPE_DELETE_DATA:
                            if (ACK_DELETE_DATA_SUCCESS.equalsIgnoreCase(ack)) {
                                Log.e(TAG, "删除一条数据成功");
                                showInfo("删除成功");
                            } else {
                                showInfo("删除失败");
                            }
                            break;
                        case CMD_TYPE_QUERY_DATA:
                            if (ack.startsWith("fbf4") && ack.length() > 8) {
                                Long i = Long.parseLong(ack.substring(4, 8), 16);
                                Log.e(TAG, "总数" + i);
                                showInfo("总数" + i);
                            }
                            break;

                    }
                    break;
                case MSG_WRITE:

                    break;
                case MSG_ERROR:
                    // 错误
                    for (SerialPortListener listener : mListeners) {
                        if (listener != null) {
                            listener.onError((String) msg.obj);
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    };


    private SerialPortManager() {
       init("/dev/ttyS7", 115200);

    }

    private static SerialPortManager INSTANCE = null;

    public static SerialPortManager getInstance() {
        if (INSTANCE == null) {
            synchronized (SerialPortManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SerialPortManager();
                }
            }
        }
        return INSTANCE;
    }

    public void setCmdType(int cmdType) {
        this.mCmdType = cmdType;
    }

    public void registerSerialPortListener(SerialPortListener listener) {
        if (!mListeners.contains(listener)) {
            mListeners.add(listener);
        }
    }

    public void unregisterSerialPortListener(SerialPortListener listener) {
        if (mListeners.contains(listener)) {
            mListeners.remove(listener);
        }
    }


    public void init(String path, int baudRate) {
        try {
            mSerialPort = new SerialPort(new File(path), baudRate, 0);
            mInputStream = mSerialPort.getInputStream();
            mOutputStream = mSerialPort.getOutputStream();
            readSerialMessageThread = new ReadSerialMessageThread();
            readSerialMessageThread.startRead();
            readSerialMessageThread.continueRead();
        } catch (Exception e) {
            Log.e(TAG, "init error: " + e.getLocalizedMessage());
            for (SerialPortListener listener : mListeners) {
                if (listener != null) {
                    listener.onError(e.getLocalizedMessage());
                }
            }
        }
    }

    public void release() {
        if (mInputStream != null) {
            try {
                mInputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (readSerialMessageThread != null) {
            readSerialMessageThread.stopRead();
        }
        if (mSerialPort != null) {
            mSerialPort.serialClose();
        }
    }

    /**
     * 串口数据读取线程
     */
    private class ReadSerialMessageThread implements Runnable {
        private boolean isStop = false;
        private boolean isPause = true;
        private Thread thread = null;

        protected void startRead() {
            if (thread == null) {
                thread = new Thread(this, "ReadSerialMessageThread");
                thread.start();
            }
        }

        protected void pauseRead() {
            isPause = true;
        }

        protected synchronized void continueRead() {
            isPause = false;
            notify();
        }

        protected synchronized void stopRead() {
            isPause = false;
            isStop = true;
            notify();
            Thread.interrupted();
        }

        @Override
        public void run() {
            while (!isStop) {
                while (isPause) {
                    synchronized (this) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            isPause = false;
                        }
                    }
                }
                try {
                    if (mInputStream == null) {
                        for (SerialPortListener listener : mListeners) {
                            if (listener != null) {
                                listener.onError("InputStream is null while reading");
                            }
                        }
                        return;
                    }
                    byte[] buffer = new byte[64];
                    int size = mInputStream.read(buffer);
                    append(buffer, size);
                } catch (Exception e) {
                    if (mHandler != null) {
                        mHandler.obtainMessage(MSG_ERROR, e.getLocalizedMessage()).sendToTarget();
                    }
                }
            }
        }

    }

    /**
     * 发送数据
     *
     * @param data {@linkplain} 数据
     */
    public void sendData(byte[] data) {
        Log.e(TAG, "准备发送数据");
        try {
            if (mOutputStream != null) {
                Log.e(TAG, "发送数据");
                mOutputStream.write(data);
            } else {
                Log.e(TAG, "mOutputStream is null，不能发送数据");
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("SerialPortManager", e.getLocalizedMessage());
        }
    }

    /**
     * 十六进制字符串转换字符串"48454C4C6F41524D" -> HELLoARM
     */
    private static String hexStr2Str(String src) {
        int len = src.length() / 2;
        byte[] ret = new byte[len];
        byte[] tmp = src.getBytes();
        for (int i = 0; i < len; i++) {
            ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
        }
        return new String(ret);
    }

    /**
     * byte数组转16进制字符串
     */
    private static String bytes2HexStr(byte[] byteArray) {
        StringBuilder sb = new StringBuilder();
        String temp = null;
        for (byte b : byteArray) {
            temp = Integer.toHexString(b & 0xFF);
            if (temp.length() == 1) {
                temp = "0" + temp;
            }
            sb.append(temp);
        }
        return sb.toString();
    }

    /**
     * 16进制字符串转byte数组
     */
    private static byte[] hexStr2Bytes(String hex) {
        byte[] data = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length() / 2; i++) {
            data[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
        }
        return data;
    }

    private static byte uniteBytes(byte src0, byte src1) {
        byte _b0 = Byte.decode("0x" + new String(new byte[]{src0})).byteValue();
        _b0 = (byte) (_b0 << 4);

        byte _b1 = Byte.decode("0x" + new String(new byte[]{src1})).byteValue();
        byte ret = (byte) (_b0 ^ _b1);

        return ret;
    }

    /**
     * 字节数组转换为字符串'H','E','L','L','O' ->"HELLO"
     */
    private static String bytes2Str(byte[] byteArray) {
        return new String(byteArray, Charset.forName("UTF-8"));
    }

    /**
     * 字符串转换为字节数组"HELLO"->'H','E','L','L','O'
     */
    private static byte[] str2bytes(String string) {
        return string.getBytes();
    }


    /**
     * 看门狗握手
     */
    public void watchDogHandshake() {
        String smd = "F0E00000000000";
        String crc = getCRC(smd);
        sendData(hexStr2Bytes("FA" + smd + crc + "FE"));
    }

    /**
     * 看门狗握手保持
     */
    public void keepDogHandshake() {
        String smd = "F0000000000000";
        String crc = getCRC(smd);
        sendData(hexStr2Bytes("FA" + smd + crc + "FE"));
    }

    /**
     * 看门狗握手中断
     */
    public void closeDogHandshake() {
        String smd = "F0E10000000000";
        String crc = getCRC(smd);
        sendData(hexStr2Bytes("FA" + smd + crc + "FE"));
    }

    /*public static void main(String[] args) {
        asyncEnd(10000);
    }*/

    private String[] cmds;

    public void updateData(String[] numbers) {
        asyncData(numbers);
        asyncHandshake();
    }

    public void next() {
        sendData(hexStr2Bytes(cmds[index]));

    }

    /**
     * 同步更新握手
     */
    public void asyncHandshake() {
        String cmd = "F1EA0000000000";
        String crc = getCRC(cmd);
        cmd = "FA" + cmd + crc + "FE";
        System.out.println("send: " + cmd);
        sendData(hexStr2Bytes(cmd));
    }

    /**
     * 生产协议数据
     *
     * @param numbers
     */
    public void asyncData(String[] numbers) {
        if (numbers == null || numbers.length == 0) return;
        cmds = new String[numbers.length];
        for (int i = 0; i < numbers.length; i++) {
            String number = numbers[i];
            if (number != null && number.length() == 12) {
                String cmd = "F1" + number;
                String crc = getCRC(cmd);
                cmd = "FA" + cmd + crc + "FE";
                cmds[i] = cmd;
            }
        }
        MAX = cmds.length;
        index = 0;
    }

    private int index;
    private int MAX;

    /**
     * 同步更新结束
     */
    public void asyncEnd(long count) {
        String s = Long.toHexString(count).toUpperCase();
        while (s.length() < 4) {
            s = "0" + s;
        }
        String cmd = "F1EF" + s + "000000";
        String crc = getCRC(cmd);
        cmd = "FA" + cmd + crc + "FE";
        System.out.println("send: " + cmd);
        sendData(hexStr2Bytes(cmd));
    }

    public void addData(String data) {

        if (data != null && data.length() == 12) {
            String cmd = "F2" + data;
            String crc = getCRC(cmd);
            cmd = "FA" + cmd + crc + "FE";
            System.out.println("addData: " + cmd);
            sendData(hexStr2Bytes(cmd));
        }
    }

    public void deleteData(String data) {
        if (data != null && data.length() == 12) {
            String cmd = "F3" + data;
            String crc = getCRC(cmd);
            cmd = "FA" + cmd + crc + "FE";
            System.out.println("deleteData: " + cmd);
            sendData(hexStr2Bytes(cmd));
        }
    }

    public void queryNumber() {
        String cmd = "FAF4000000000000F4FE";
        System.out.println("queryNumber: " + cmd);
        sendData(hexStr2Bytes(cmd));
    }

    /**
     * 计算校验和
     *
     * @param data 16进制数据
     * @return 校验和
     */
    public static String getCRC(String data) {
        long sum = 0;
        for (int i = 0; i < data.length() / 2; i++) {
            String b = data.substring(i * 2, i * 2 + 2);
            sum += Long.parseLong(b, 16);
        }
        String sumHex = Long.toHexString(sum).toUpperCase();
        System.out.println("和: " + sumHex);
        String crc = sumHex.substring(sumHex.length() - 2, sumHex.length());
        System.out.println("校验和: " + crc);
        return crc;
    }

    /**
     * 组合数据
     */
    private void append(byte[] buffer, int size) {
        if (size <= 0 || size > SIZE) return;
        Log.i("SerialPortManager", "Streaming size: " + size + ", mCount: " + mCount);
        if (size == SIZE) {
            // 本次读取的数据正好是5个字节
            mData = new byte[SIZE];
            System.arraycopy(buffer, 0, mData, 0, size);
            String result = bytes2HexStr(mData);
            // 发送消息
            if (mHandler != null) {
                mHandler.obtainMessage(MSG_READ, result).sendToTarget();
            }
        } else {
            // 本次读取的数据不够6个字节，需要组合
            final long time = System.currentTimeMillis();
            mCount += size;
            if (mCount > SIZE) {
                mCount = 0;
                return;
            }
            if (time - mLastTime > TIME_OUT) {
                // 两次流的间隔时间大于设定的超时时间，视为一条新的数据
                mData = new byte[SIZE];
                System.arraycopy(buffer, 0, mData, 0, size);
            } else {
                // 两次流的间隔时间小于设定的超时时间，视为同一条数据
                System.arraycopy(buffer, 0, mData, mCount - size, size);
            }
            mLastTime = time;
            if (mCount == SIZE) {
                // 已有一条完整的数据
                String result = bytes2HexStr(mData);
                mCount = 0;
                // 发送消息
                if (mHandler != null) {
                    mHandler.obtainMessage(MSG_READ, result).sendToTarget();
                }
            }
        }
    }

    public ShowInfoView showInfoView;

    public void showInfo(String info) {
        if (showInfoView != null) {
            showInfoView.showInfo(info);
        }
    }

    public interface ShowInfoView {
        void showInfo(String info);
    }
}